Polymorphism can be effectively implemented using composition by creating classes that contain instances of other classes, allowing for flexible and reusable code without the tight coupling of inheritance.
Composition is a design principle in object-oriented programming where a class is composed of one or more objects from other classes. This allows for building complex types by combining simpler ones, promoting code reuse and flexibility. Unlike inheritance, which creates a tight coupling between parent and child classes, composition allows for a more modular approach where components can be easily replaced or modified without affecting other parts of the system.
Define Interfaces: Start by defining interfaces that declare the methods you want to implement. This allows different classes to provide their own implementations of these methods.
public interface Shape {
void draw();
}
Implement Classes: Create classes that implement the interface. Each class can have its own unique behavior while adhering to the same interface.
public class Circle implements Shape {
public void draw() {
System.out.println("Drawing a Circle");
}
}
public class Square implements Shape {
public void draw() {
System.out.println("Drawing a Square");
}
}
Use Composition: Create a class that uses composition to include instances of the interface type. This class can then call the methods on the composed objects, achieving polymorphic behavior.
public class Drawing {
private List<Shape> shapes = new ArrayList<>();
public void addShape(Shape shape) {
shapes.add(shape);
}
public void drawAll() {
for(Shape shape : shapes) {
shape.draw(); // Polymorphic call
}
}
}
Demonstrate Usage: You can now create a Drawing object, add different shapes to it, and call the drawAll method to see polymorphism in action.
public class Main {
public static void main(String[] args) {
Drawing drawing = new Drawing();
drawing.addShape(new Circle());
drawing.addShape(new Square());
drawing.drawAll(); // Outputs: Drawing a Circle, Drawing a Square
}
}
Loose Coupling: Composition allows for loosely coupled designs, making it easier to change or replace components without affecting the entire system.
Flexibility: You can easily add new behaviors by creating new classes that implement the same interface, without modifying existing code.
Reusability: Components can be reused across different classes, promoting code reuse and reducing redundancy.
By using composition, you can achieve polymorphic behavior while maintaining a clean and flexible code structure, making it a preferred approach in many scenarios.